package vsy.buzzwordbingo.model.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import vsy.buzzwordbingo.model.Game;
import vsy.buzzwordbingo.model.GameBoard;
import vsy.buzzwordbingo.model.Player;

public class GameBoardImpl implements GameBoard {
	private int size;
	private String[][] filledBoard;
	private boolean[][] markedBoard;
	private Game game;
	private Player player;
	private List<String> buzzwords;
	private long lastPolled;
	private boolean finished;
	
	public GameBoardImpl(Player player, Game game, List<String> buzzwords, int size) {
		this.player = player;
		this.game = game;
		this.size=size;
		initBoard(buzzwords);
		lastPolled = updateLastPollTime();
	}

	private void initBoard(List<String> buzzwords) {
		List<String> buzzwordsCopy = new ArrayList<String>(buzzwords.size()); 
		buzzwordsCopy.addAll(buzzwords);
		Collections.shuffle(buzzwordsCopy);
		this.buzzwords = Collections.unmodifiableList(buzzwordsCopy);
		
		// initialize Gamebaord that should be filled
		filledBoard = new String[size][size];
		markedBoard = new boolean[size][size];
		if ((size % 2) > 0) {
			int center = (size / 2);
			markedBoard[center][center] = true;
			filledBoard[center][center] = "JOKER";
			
			int count = 0;
			for (String word : this.buzzwords) {
				if (!markedBoard[(count / this.size)][(count % this.size)]) {
					filledBoard[(count / this.size)][(count % this.size)] = word;
				} else {
					count++;
					filledBoard[(count / this.size)][(count % this.size)] = word;
				}
				count++;
			}
		} else {
			int count = 0;
			for (String word : this.buzzwords) {
					filledBoard[(count / this.size)][(count % this.size)] = word;
				count++;
			}
		}
	}
	
	@Override
	public int getSize() {
		return size;
	}

	@Override
	public Game getGame() {
		return game;
	}

	@Override
	public Player getPlayer() {
		return player;
	}

	@Override
	public List<String> getBuzzwords() {
		return buzzwords;
	}
	
	@Override
	public String getBuzzword(int x, int y) {
		return filledBoard[x][y];
	}

	@Override
	public boolean isFinished() {
		return this.finished;
	}
	
	// Checks every word of the column to be checked
	private boolean checkColumn(int col){
		boolean bingo = true;
		for (int row = 0; row < this.size; row++) {
			if (!markedBoard[row][col]) {
				bingo = false;
			}
		}
		return bingo;
	}
	
	private boolean checkDiagonale(){
		boolean bingo = true;
		boolean altBingo = true;
		
		//check diagonale from top left to bottom right
		for (int coord = 0; coord < this.size; coord++){
				if(!markedBoard[coord][coord]){
					bingo = false;
			}
		}
		// check diagonale from right top to bottom left
		for (int coord = 0; coord < this.size; coord++){
				if(!markedBoard[coord][((this.size - 1) - coord)]){
					altBingo = false;
				}
		}
		if (bingo || altBingo) {
			return true;
		} else return false;
	}
	
	// Checks every word of the row to be checked
	private boolean checkRow(int row){
		boolean bingo = true;
		for (int col = 0; col < this.size; col++) {
			if (!markedBoard[row][col]) {
				bingo = false;
			}
		}
		return bingo;
	}	

	@Override
	public boolean isMarked(int row, int col) {
		return markedBoard[row][col];
	}

	@Override
	/**
	 * The method marks the current selected word with the
	 * given value. If the value is FALSE, the word will
	 * be unchecked. If the value is TRUE, the word will
	 * be checked and some internal private methods check
	 * if row, column or diagonal line of checked words is
	 * reached.*/
	public boolean setMarked(int row, int col, boolean value) {
		markedBoard[row][col]= value;
		if (value && (checkColumn(col) || checkRow(row) || checkDiagonale())) {
			this.finished = true;
		}
		return isFinished();
	}

	@Override
	public long getLastPollTime() {
		return lastPolled;
	}

	@Override
	public long updateLastPollTime() {
		return (lastPolled = System.currentTimeMillis());
	}
	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof GameBoard))
			return false;
		
		GameBoard board = (GameBoard)obj;
		return (board.getGame().equals(game)) &&
			   (board.getPlayer().equals(player)) &&
			   (board.getBuzzwords().equals(buzzwords));
	}
	
	@Override
	public int hashCode() {
		return (game.hashCode() + player.hashCode()) % Integer.MAX_VALUE;
	}
	
	@Override
	public String toString() {
		return "Game: " + game.toString() + "\nPlayer: " + player.toString();
	}
}
